﻿namespace LambdaCalc.Core.Tests

open System
open NUnit.Framework

open LambdaCalc.Core

[<TestFixture>]
type ``test core-operations: isVariableFreeIn``() = 

    [<Test>]
    member test.``x is free in Atom x``() =
        let input = Atom "x"
        let result = isVariableFreeIn input "x"
        Assert.IsTrue(result)

    [<Test>]
    member test.``x and y is free in xy``() =
        let input = Application(Atom "x", Atom "y")
        let result = isVariableFreeIn input "x" && isVariableFreeIn input "y"
        Assert.IsTrue(result)

    [<Test>]
    member test.``y is free in \x.y``() =
        let input = Abstraction("x", Atom "y")
        let expectedOutput = Set.singleton "y"
        let result = isVariableFreeIn input "y"
        Assert.IsTrue(result)

    [<Test>]
    member test.``x is not-free in \x.x``() =
        let input = Abstraction("x", Atom "x")
        let expectedOutput = Set.empty
        let result = isVariableFreeIn input "x"
        Assert.IsFalse(result)

    [<Test>]
    member test.``(((xv)(\y.(\z.(yv))))w) has FV x,v,w``() =
        let input = Application( Application (
                                   Application(Atom "x", Atom "v"),
                                   Abstraction ("y",
                                     Abstraction("z", Application(Atom "y", Atom "v")))),
                                 Atom "w")
        let result = isVariableFreeIn input "x"
                     && isVariableFreeIn input "v"
                     && isVariableFreeIn input "w"
        Assert.IsTrue(result)

    [<Test>]
    member test.``findUnusedVariableFor returns it's argument if it's not in the free Variables set``() =
        let freeVars = Set.ofList ["x"]
        let input = "y"
        let result = findUnusedVariableFor freeVars input
        Assert.AreEqual("y", result)

    [<Test>]
    member test.``findUnusedVariableFor prepends '_' to it's argument if it's in the free Variables set``() =
        let freeVars = Set.ofList ["x"]
        let input = "x"
        let result = findUnusedVariableFor freeVars input
        Assert.AreEqual("_x", result)

    [<Test>]
    member test.``findUnusedVariableFor prepends '_' to it's argument as long as it is in the free Variables set``() =
        let freeVars = Set.ofList ["x"; "_x"; "__x"]
        let input = "x"
        let result = findUnusedVariableFor freeVars input
        Assert.AreEqual("___x", result)